/**
 * The hyperstripes javascript global namespace
 */
var hs = {};

/**
 * HyperStripes Ajax System.
 * Provides encapsulation of all ajax functionality and procedures
 */
hs.ajax = function() {

    /**
         * Determine if the current browser is part of Microsoft's failed attempt at
         * standards modification.
         * @ignore
         */
    var isIE = function isIE() {
        if (typeof isIECache !== "undefined") {
            return isIECache;
        }
        isIECache =
        document.all && window.ActiveXObject &&
        navigator.userAgent.toLowerCase().indexOf("msie") > -1 &&
        navigator.userAgent.toLowerCase().indexOf("opera") == -1;
        return isIECache;
    };
    /**
     * @ignore
     */
    var getTransport = function getTransport() {
        if ( window.XMLHttpRequest ) { // mozilla, safari, opera, chrome, ie7
            return new XMLHttpRequest();
        } else if ( window.ActiveXObject ) { // ie, avant, aol explorer
            try {
                return new ActiveXObject("Msxml2.XMLHTTP")
            } // ie6
            catch (e) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP")
                } // ie5
                catch(e) {
                    throw new Error('Could not create an XHR object.');
                }
            }
        }
        else
        {
            throw new Error('Could not create an XHR object.');
        }
    };

    /**
     * Find instance of passed String via getElementById
     * @ignore
     */
    var $ = function $() {
        var results = [], element;
        for (var i = 0; i < arguments.length; i++) {
            element = arguments[i];
            if (typeof element == 'string') {
                element = document.getElementById(element);
            }
            results.push(element);
        }
        return results.length > 1 ? results : results[0];
    };

    /**
     * Methods for suporting ajax Functionality
     *
     */
    var getForm = function getForm(element, any) {
        if (element) {
            var form = $(element);
            while (form) {

                if (form.nodeName && (form.nodeName.toLowerCase() == 'form')) {
                    return form;
                }

                if (form.form) {
                    return form.form;
                }

                if (form.parentNode) {
                    form = form.parentNode;
                } else {
                    form = null;
                }
            }
            if( any == true )
            {
                form = document.forms[0];
            }
            return form;
        }
        return null;
    };

    /**
     * Queing system used for queing ajax requests
     */
    var Queue = new function Queue() {} ();

    /**
     * The ajax engine is the handler of the actual XmlHttpRequestObject
     */
    var HSAjaxEngine = function HSAjaxEngine() {

        var req = {};                // Request Object

        req.url = null;                // Request URL

        req.parameters = {};           // Parameters For GET or POST

        req.queryString = null;        // Encoded Data For GET or POST

        req.context = {};              // Context of request and response

        req.context.sourceElement = null;   // Source of this request

        req.context.sourceEvent = null;    // Event handler for request

        req.context.sourceForm = null;     // Form that's the context for this request

        req.method = null;             // GET or POST

        req.async = true;              // Default - Asynchronous

        // Get an XMLHttpRequest Handle
        req.xhr = getTransport();
        if (req.xmlReq === null) {
            return null;
        }

        function moop() {}

        req.xhr.onreadystatechange = function() {
            req.context.sourceEvent.preventDefault();
            if (req.xhr.readyState === 4) {
                req.onComplete();
                req.xhr.onreadystatechange = moop;
                req.xhr = null;
            }
        };

        /**
         * This function is called when the response is received
         * @ignore
         */
        req.onComplete = function onComplete() {
            if (req.xhr.status && (req.xhr.status >= 200 && req.xhr.status < 300)) {
                //                sendEvent(req.xmlReq, req.context, "complete");
                hs.ajax.response(req.xhr, req.context);
            } else {
                //                sendEvent(req.xmlReq, req.context, "complete");
                //                sendError(req.xmlReq, req.context, "httpError");
                alert("failed")
            }
        }

        req.setupArguments = function(args) {
            for (var i in args) {
                if (args.hasOwnProperty(i)) {
                    if (typeof req[i] === 'undefined') {
                        req.parameters[i] = args[i];
                    } else {
                        req[i] = args[i];
                    }
                }
            }
        };

        /**
         *
         * Dispatches the ajax request
         **/
        req.sendRequest = function() {

            var content = null; // For POST requests, to hold query string
            for (var i in req.parameters) {
                if (req.parameters.hasOwnProperty(i)) {
                    if (req.queryString.length > 0) {
                        req.queryString += "&";
                    }
                    req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
                }
            }

            //Only get or post suported
            if (req.method === "GET") {
                if (req.queryString.length > 0) {
                    req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
                }
            }
            req.xhr.open(req.method, req.url, req.async);

            if (req.method === "POST") {
                if (typeof req.xhr.setRequestHeader !== 'undefined') {
                    req.xhr.setRequestHeader('HS-AJAX-Request', 'partial/ajax');
                    req.xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                }
                content = req.queryString;
            }

            req.xhr.send(content);

            return false;
        }
        return req;
    };

    /**
         * Deletes node
         * @param node
         * @ignore
         */
    var deleteNode = function deleteNode(node) {
        if (!node) {
            return;
        }
        if (!isIE()) {
            // nothing special required
            if (!node.parentNode) {
                // if there's no parent, there's nothing to do
                return;
            }
            node.parentNode.removeChild(node);
            return;
        }
        // The rest of this code is specialcasing for IE
        if (node.nodeName.toLowerCase() === "body") {
            // special case for removing body under IE.
            deleteChildren(node);
            try {
                node.outerHTML = '';
            } catch (ex) {
            // fails under some circumstances, but not in RI
            // supplied responses.  If we've gotten here, it's
            // fairly safe to leave a lingering body tag rather than
            // fail outright
            }
            return;
        }
        var temp = node.ownerDocument.createElement('div');
        var parent = node.parentNode;
        temp.appendChild(parent.removeChild(node));
        // Now clean up the temporary element
        try {
            temp.outerHTML = ''; //prevent leak in IE
        } catch (ex) {
        // at least we tried.  Fails in some circumstances,
        // but not in RI supplied responses.  Better to leave a lingering
        // temporary div than to fail outright.
        }
    };

    /**
     * Deletes all children of a node
     * @param node
     * @ignore
     */
    var deleteChildren = function deleteChildren(node) {
        if (!node) {
            return;
        }
        for (var x = node.childNodes.length - 1; x >= 0; x--) { //delete all of node's children
            var childNode = node.childNodes[x];
            deleteNode(childNode);
        }
    };

    /**
     * Do update.
     * @param element element to update
     * @param context context of request
     * @ignore
     */
    var doUpdate = function doUpdate(element, context) {
    };

    /**
     * Do update.
     * @param element element to update
     * @ignore
     */
    var doDelete = function doDelete(element) {
        var id = element.id;
        var target = $(id);
        deleteNode(target);
    };

    /**
     * Do update.
     * @param element element to update
     * @ignore
     */
    var doInsert = function doInsert(element) {
        if( element.before && element.after)
        {
            throw new Error("only one of either before or after can be specified. not both")
        }
        if( !element.before && !element.after)
        {
            throw new Error("one of either before or after MUST be specified.")
        }
        var tempElement = document.createElement("span");
        var attributes = element.attributes;
        for ( var i in attributes )
        {
            tempElement.setAttribute(attributes[i].name, attributes[i].value);
        }
        tempElement.innerHTML = element.elementValue;
        if (element.after) {
            var afterElement = document.getElementById(element.after.id)
            afterElement.parentNode.insertBefore( tempElement.firstChild, afterElement.nextSibling );
        }
        else {
            var beforeElement = document.getElementById(element.before.id)
            beforeElement.parentNode.insertBefore( tempElement.firstChild, beforeElement);
        }
        deleteNode(tempElement);

    };

    /**
     * Methods exposed by the Ajax System to the Hyperstripes application
     * These are the methods that can be called from the html code
     */
    return {

        /**
         * For registering Error Handlers
         */
        addOnError: function addOnError(callback) {
        },

        /**
         *
         * For adding Event listeners
         */
        addOnEvent: function addOnEvent(callback) {
        },

        /***
         *
         * Ajax request handler for invoking ajax
         * requests by the hyperstripes applocation
         */
        request: function request(source, event, options) {
            var element, form;   //  Element variables

            // set up the element based on source
            if (typeof source === 'string') {
                element = document.getElementById(source);
            } else if (typeof source === 'object') {
                element = source;
            } else {
                throw new Error("hs.ajax.request: the source of the request must be an element on the page");
            }
            // attempt to handle case of name unset
            // this might be true in a badly written composite component
            if (!element.name) {
                element.name = element.id;
            }

            var args = {};

            var queryString;

            var all, none;

            //Check if element is within a form
            form = getForm( element, true );

            if( element.type == "a" && source.href != "" ) {
                args["url"] = element.href;
                args["hs.request.source"] = element.getAttribute("id");
            } else if( form ){
                args["url"] = form.action;
                queryString = hs.getFormValues(form);
                args["hs.request.source"] = form.getAttribute("id");
            }
            else{
                throw new Error("hs.ajax.request: Method must be a form or an anchor");
            }

            args["hs.request.submittype"] = "ajax";
            args["method"] = "POST";

            if (options.render) {
                none = options.render.search(/@none/);
                if (none < 0) {
                    all = options.render.search(/@all/);
                    if (all < 0) {
                        options.render = options.render.replace("@this", element.id);
                        options.render = options.render.replace("@form", form.id);
                    } else {
                        options.render = "@all";
                    }
                    args["hs.request.clients"] = options.render;
                }
            }

            var ajaxEngine = new HSAjaxEngine();
            ajaxEngine.setupArguments(args);
            ajaxEngine.queryString = queryString;

            ajaxEngine.context.sourceElement = element;
            ajaxEngine.context.sourceEvent = event;
            ajaxEngine.context.sourceForm = form;
            ajaxEngine.sendRequest();
        },

        /**
         *
         * Processes the ajax request when the response is received
         *
         */
        response: function response(request, context) {

            var doc = eval('(' + request.responseText + ')');

            if (doc.error) { // it's an error
                var myerror = doc.error;
                alert(myerror.errorMessage);
                return;
            }

            if (doc.redirect) {
                //                window.location = responseType.getAttribute("url");
                var myRedirect = doc.redirect;
                window.location = myRedirect.url;
                return;
            }

            try {
                var mychanges = doc.changes;
                var myupdates = mychanges.updates;
                if(myupdates)
                {
                    for (var i in myupdates ) {
                        doUpdate(myupdates[i], context);
                    }
                }

                var myinserts = mychanges.inserts;
                if(myinserts)
                {
                    for (var j in myinserts ) {
                        doInsert(myinserts[j]);
                    }
                }

                var myDeletes = mychanges.deletes;
                if(myDeletes)
                {
                    for (var k in myDeletes ) {
                        doDelete(myDeletes[k]);
                    }
                }
            } catch (ex) {
                alert( ex.message);
                return;
            }
        //TODO
        //sendEvent(request, context, "success");
        }
    };
}();

hs.getFormValues = function(form)
{
    if (!form  || form.tagName.toLowerCase() != "form") {
        throw new Error("hs.getFormValues:  Element Passed is not a form");
    }
    var formElements = form.elements;
    var len = formElements.length;
    var qString = [];

    var addField = function(name, value) {
        var tmpStr = "";
        if (qString.length > 0) {
            tmpStr = "&";
        }
        tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value);
        qString.push(tmpStr);
    };
    for (var i = 0; i < len; i++) {
        var el = formElements[i];
        if (!el.disabled) {
            switch (el.type) {
                case 'text':
                case 'submit':
                case 'password':
                case 'hidden':
                case 'textarea':
                    addField(el.name, el.value);
                    break;
                case 'select-one':
                    if (el.selectedIndex >= 0) {
                        addField(el.name, el.options[el.selectedIndex].value);
                    }
                    break;
                case 'select-multiple':
                    for (var j = 0; j < el.options.length; j++) {
                        if (el.options[j].selected) {
                            addField(el.name, el.options[j].value);
                        }
                    }
                    break;
                case 'checkbox':
                case 'radio':
                    if (el.checked) {
                        addField(el.name, el.value);
                    }
                    break;
            }
        }
    }
    return qString.join("");
};

/**
 * HyperStripes Utitlities System
 */
hs.util = {
    };
